// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package os

import (
	"io/fs"
	"sort"
)

type readdirMode int

const (
	readdirName readdirMode = iota
	readdirDirEntry
	readdirFileInfo
)

// Readdir读取与文件关联的目录的内容，
// 返回最多n个FileInfo值的一个片段，Lstat将按目录顺序返回
// 。对同一文件的后续调用将产生
// 更多文件信息。
// 
// 如果n>0，Readdir最多返回n个FileInfo结构。在这种情况下，如果
// Readdir返回一个空切片，它将返回一个非零错误
// 解释原因。在目录的末尾，错误是io。EOF。
// 
// 如果n<=0，Readdir将从
// 中的目录返回所有文件信息，只返回一个片段。在这种情况下，如果Readdir成功（读取所有
// 直到目录的末尾），它将返回切片和
// nil错误。如果在
// 目录结束之前遇到错误，Readdir将返回在该点
// 之前读取的文件信息，并返回非零错误。
// 
// 更高效的ReadDir方法可以更好地为大多数客户服务。
func (f *File) Readdir(n int) ([]FileInfo, error) {
	if f == nil {
		return nil, ErrInvalid
	}
	_, _, infos, err := f.readdir(n, readdirFileInfo)
	if infos == nil {
		// Readdir历来都会返回一个非nil空片，从不为nil，
		// 即使出现错误（上述与nil接收器的误用除外）。
		// 保持这种状态以避免打扰过于敏感的来电者。
		infos = []FileInfo{}
	}
	return infos, err
}

// Readdirnames读取与文件
// 关联的目录的内容，并按目录顺序返回目录中最多n个文件名的一部分，
// 。对同一文件的后续调用将产生
// 其他名称。
// 
// 如果n>0，Readdirnames最多返回n个名称。在这种情况下，如果
// Readdirnames返回一个空切片，它将返回一个非零错误
// 解释原因。在目录的末尾，错误是io。EOF。
// 
// 如果n<=0，Readdirnames将从
// 中的目录返回所有名称。在这种情况下，如果Readdirnames成功（读取所有
// 直到目录的末尾），它将返回切片和
// nil错误。如果在
// 目录结束之前遇到错误，Readdirnames将返回在此之前读取的名称，而
// 将返回非零错误。
func (f *File) Readdirnames(n int) (names []string, err error) {
	if f == nil {
		return nil, ErrInvalid
	}
	names, _, _, err = f.readdir(n, readdirName)
	if names == nil {
		// Readdirnames历来都会返回一个非零的空片，从不为零，
		// 即使出现错误（上述与零接收器的误用除外）。
		// 保持这种状态以避免打扰过于敏感的来电者。
		names = []string{}
	}
	return names, err
}

// DirEntry是从目录
// （使用ReadDir函数或文件的ReadDir方法）读取的条目。
type DirEntry = fs.DirEntry

// ReadDir读取与文件f 
// 关联的目录的内容，并按目录顺序返回一段DirEntry值。
// 对同一个文件的后续调用将在目录中生成更晚的DirEntry记录。
// 
// 如果n>0，ReadDir最多返回n条DirEntry记录。
// 在本例中，如果ReadDir返回一个空切片，它将返回一个解释原因的错误。
// 在目录的末尾，错误是io。EOF。
// 
// 如果n<=0，ReadDir返回目录中剩余的所有DirEntry记录。
// 当它成功时，返回一个nil错误（不是io.EOF）。
func (f *File) ReadDir(n int) ([]DirEntry, error) {
	if f == nil {
		return nil, ErrInvalid
	}
	_, dirents, _, err := f.readdir(n, readdirDirEntry)
	if dirents == nil {
		// 匹配Readdir和Readdirnames：不返回零片。
		dirents = []DirEntry{}
	}
	return dirents, err
}

// testingForceReadDirLstat强制ReadDir调用Lstat，以测试代码路径。
// 否则在某些Unix系统上很难引发这种情况。
var testingForceReadDirLstat bool

// ReadDir读取命名目录，
// 返回按文件名排序的所有目录项。
// 如果读取目录时发生错误，
// ReadDir将返回在错误发生前能够读取的条目，
// 以及错误。
func ReadDir(name string) ([]DirEntry, error) {
	f, err := Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	dirs, err := f.ReadDir(-1)
	sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
	return dirs, err
}
